import vue from 'eslint-plugin-vue'
import { mergeProcessors } from 'eslint-merge-processors'
import tseslint from 'typescript-eslint'
import { FlatConfig, type ResolveOptions, Rules } from '../types'
import { interopDefault } from '../utils'

const isVue2 = async (userVersion?: number) => {
  if (userVersion) {
    return userVersion === 2
  }
  try {
    const { version } = await import('vue')
    return /^2/.test(version)
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
  } catch (e) {
    return false
  }
}

const essential = (isVue2: boolean): Rules => ({
  ...(isVue2
    ? vue.configs['vue2-essential'].rules
    : vue.configs['flat/essential'].map((c) => c.rules).reduce((acc, c) => ({ ...acc, ...c }), {})),
  'vue/multi-word-component-names': 'off',
})

const stronglyRecommended = (): Rules => ({
  'vue/html-self-closing': [
    'error',
    {
      html: {
        void: 'always',
        normal: 'always',
        component: 'always',
      },
      svg: 'always',
      math: 'always',
    },
  ],
  'vue/prop-name-casing': ['error', 'camelCase'],
  'vue/v-bind-style': ['error', 'shorthand'],
  'vue/v-on-style': ['error', 'shorthand'],
  'vue/v-slot-style': [
    'error',
    {
      atComponent: 'v-slot',
      default: 'shorthand',
      named: 'shorthand',
    },
  ],
})

const recommended = (): Rules => ({
  'vue/attributes-order': 'warn',
  'vue/no-lone-template': 'warn',
  'vue/this-in-template': 'error',
})

const unCategorized = (isVue2: boolean, isTS: boolean): Rules => ({
  'vue/block-order': [
    'error',
    {
      order: ['script[setup]', ['template', 'script:not([setup])'], 'style'],
    },
  ],
  'vue/no-duplicate-attr-inheritance': 'error',
  'vue/no-v-text': 'error',
  ...(isTS
    ? {
        'vue/block-lang': [
          'error',
          {
            script: {
              lang: ['ts', 'tsx'],
            },
          },
        ],
      }
    : {}),
  ...(isVue2
    ? {}
    : {
        'vue/component-api-style': ['error', ['script-setup', 'composition']],
        'vue/define-macros-order': [
          'error',
          {
            order: ['defineOptions', 'defineProps', 'defineEmits', 'defineModel', 'defineSlots'],
            defineExposeLast: true,
          },
        ],
        'vue/next-tick-style': ['error', 'promise'],
        'vue/require-macro-variable-name': [
          'error',
          {
            defineProps: 'props',
            defineEmits: 'emit',
            defineSlots: 'slots',
            useSlots: 'slots',
            useAttrs: 'attrs',
          },
        ],
        'vue/valid-define-options': 'error',
      }),
})

export default async (options: ResolveOptions): Promise<FlatConfig[]> => {
  const {
    vue: { overrides = {}, version, vueBlocks },
    typescript: { enable: isTS },
  } = options

  const [parserVue, processorVueBlocks, vue2] = await Promise.all([
    interopDefault(import('vue-eslint-parser')),
    interopDefault<(typeof import('eslint-processor-vue-blocks'))['default']>(
      import('eslint-processor-vue-blocks')
    ),
    isVue2(version),
  ] as const)

  return [
    {
      name: 'vill-v/vue/setup',
      plugins: {
        vue,
      },
    },
    {
      name: 'vill-v/vue/rules',
      files: ['**/*.vue'],
      languageOptions: {
        parser: parserVue as any,
        parserOptions: {
          ecmaFeatures: {
            jsx: true,
          },
          extraFileExtensions: ['.vue'],
          sourceType: 'module',
          parser: isTS ? tseslint.parser : null,
        },
      },
      processor:
        vueBlocks === false
          ? vue.processors['.vue']
          : mergeProcessors([
              vue.processors['.vue'],
              processorVueBlocks({
                blocks: {
                  styles: true,
                  ...(vueBlocks === true ? {} : vueBlocks),
                },
              }),
            ]),
      rules: {
        ...(vue.configs.base.rules as any),
        'no-unused-vars': 'off',
        'no-undef': 'off',
        '@typescript-eslint/no-unused-vars': 'off',
        ...essential(vue2),
        ...stronglyRecommended(),
        ...recommended(),
        ...unCategorized(vue2, isTS),
        ...overrides,
      },
    },
  ]
}
